package com.google.code.sms.service;

import com.google.code.sms.Logger;
import com.google.code.sms.domain.MediaElement;
import com.google.code.sms.io.TranscodeInputStream;
import java.awt.Dimension;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

/**
 * Provides services for transcoding media.
 * 
 * Supported Video Formats: matroska, mp4, webm
 * Supported Audio Formats: mp4, flac, mp3, ogg, wav
 * 
 * Supported Video Codecs: h264, vp8
 * Supported Audio Codecs: aac, ac3, flac, mp3, vorbis, wav
 * 
 * @author Scott Ware
 * @see TranscodeInputStream
 */
public class TranscodingService {

    private static final Logger LOG = Logger.getLogger("TranscodingService");
    
    private static final String TRANSCODER = "ffmpeg";
    
    private static final int AAC_DEFAULT = 128;
    private static final int AAC_LOW = 128;
    private static final int AAC_MEDIUM = 192;
    private static final int AAC_HIGH = 256;
    private static final int MP3_DEFAULT = 128;
    private static final int MP3_LOW = 128;
    private static final int MP3_MEDIUM = 192;
    private static final int MP3_HIGH = 256;
    private static final int OGG_DEFAULT = 128;
    private static final int OGG_LOW = 128;
    private static final int OGG_MEDIUM = 192;
    private static final int OGG_HIGH = 256;
    
    /**
     * Returns a transcoded input stream for the given media element.
     */
    public InputStream getTranscodedInputStream(Parameters parameters) throws IOException
    {
        try
        {
            return createTranscodedInputStream(parameters);
        }
        catch (Exception x)
        {
            LOG.warn("Failed to transcode file.");
        }

        return new FileInputStream(parameters.getMediaElement().getFile());
    }

    /**
     * Returns an input stream by applying the given transcoding to the given
     * media element.
     *
     * @param parameters Transcoding parameters.
     * @return The transcoded input stream.
     * @throws IOException If an I/O error occurs.
     */
    private InputStream createTranscodedInputStream(Parameters parameters)
            throws IOException
    {
        List<String> transcodeCommand = new LinkedList<>();
        MediaElement mediaElement = parameters.getMediaElement();
        
        // Initial parameter checks.
        if(parameters.getFormat() == null)
        {
            LOG.warn("Transcode format is missing!");
            return null;
        }
        
        if(!isSupportedFormat(parameters.getFormat()))
        {
            LOG.warn("Transcode format is not supported!");
            return null;
        }
        
        // Set transcoder
        transcodeCommand.add(getTranscoder().getPath());
        
        // No Console Output
        transcodeCommand.add("-loglevel");
        transcodeCommand.add("quiet");
                
        switch(mediaElement.getMediaElementType())
        {
            case VIDEO:
                
                // Offset
                if(parameters.getOffset() != null)
                {
                    transcodeCommand.add("-ss");
                    transcodeCommand.add(String.valueOf(parameters.getOffset()));
                }
                
                // Input File
                transcodeCommand.add("-i");
                transcodeCommand.add(mediaElement.getFile().getAbsolutePath());
                
                // Ignore embedded subtitles.
                transcodeCommand.add("-sn");
                
                // Video Quality
                if(parameters.getVideoQuality() != null)
                {
                    Dimension resolution = getSuitableVideoResolution(parameters.getVideoQuality(), mediaElement);
                    Integer bitRate = getSuitableVideoBitRate(resolution);
                    
                    // Video Bit Rate
                    transcodeCommand.add("-b:v");
                    transcodeCommand.add(String.valueOf(bitRate) + "k");
                    
                    // Video Resolution
                    transcodeCommand.add("-s");
                    transcodeCommand.add(String.valueOf(resolution.width) + "x" + String.valueOf(resolution.height));
                }
                
                // Audio Codec
                if(parameters.getAudioCodec() == null)
                {
                    // See if original codec is suitable.
                    String originalCodec = parameters.getMediaElement().getMetaData().getAudioCodec();
                    
                    // Check if format supports the original codec.
                    if(isSupportedCodec(originalCodec, parameters.getFormat()))
                    {
                        parameters.setAudioCodec(originalCodec);
                        
                        // Check if other parameters stop us copying the original audio stream.
                        if(isAudioTranscodeRequired(parameters))
                        {
                            // Get a suitable audio codec if original does not meet other parameters.
                            if(getSuitableAudioCodec(parameters.getFormat()) != null)
                            {
                                parameters.setAudioCodec(getSuitableAudioCodec(parameters.getFormat()));
                            }
                        }
                        // Copy the original audio stream.
                        else
                        {
                            parameters.setAudioCodec("copy");
                            transcodeCommand.add("-c:a");
                            transcodeCommand.add("copy");
                        }
                    }
                    else
                    {
                        // Get a suitable audio codec if original does not meet other parameters.
                        if(getSuitableAudioCodec(parameters.getFormat()) != null)
                        {
                            parameters.setAudioCodec(getSuitableAudioCodec(parameters.getFormat()));
                        }
                    }
                }
                
                // If not using the original stream, determine audio codec and other parameters.
                if(!parameters.getAudioCodec().equals("copy"))
                {
                    // If transcode is not required, simply copy audio stream.
                    if(isAudioTranscodeRequired(parameters))
                    {
                        if(getAudioCodecCommands(parameters) != null)
                        {
                            transcodeCommand.addAll(getAudioCodecCommands(parameters));
                        }
                        
                        // Audio Channel Count / Quality
                        if(getTranscodedChannelCount(parameters) != null)
                        {
                            Integer channelCount = getTranscodedChannelCount(parameters);
                            
                            // Test whether downmixing is necessary.
                            if(channelCount < getChannelCount(parameters.getMediaElement().getMetaData().getAudioConfiguration()))
                            {
                                transcodeCommand.add("-ac");
                                transcodeCommand.add(String.valueOf(channelCount));
                                
                                // Increase volume on downmix
                                if(channelCount == 2)
                                {
                                    transcodeCommand.add("-vol");
                                    transcodeCommand.add("256");
                                }
                            }

                            // Stereo Audio Quality
                            if(channelCount == 2 && getAudioQualityCommands(parameters) != null)
                            {
                                transcodeCommand.addAll(getAudioQualityCommands(parameters));
                            }
                        }

                        // Audio Sample Rate
                        if(parameters.getMaxSampleRate() != null)
                        {
                            if(parameters.getMaxSampleRate() < mediaElement.getMetaData().getAudioSampleRate())
                            {
                                transcodeCommand.add("-ar");
                                transcodeCommand.add(String.valueOf(parameters.getMaxSampleRate()));
                            }
                        }
                    }
                    // Transcoding of audio is not needed.
                    else
                    {
                        transcodeCommand.add("-c:a");
                        transcodeCommand.add("copy");
                    }
                }
                
                // Media Format
                transcodeCommand.add("-f");
                transcodeCommand.add(parameters.getFormat());

                // Video Codec
                if(parameters.getVideoCodec() == null)
                {
                    // See if original codec is suitable.
                    String originalCodec = parameters.getMediaElement().getMetaData().getVideoCodec();
                    
                    // Check if format supports the original codec.
                    if(isSupportedCodec(originalCodec, parameters.getFormat()))
                    {
                        parameters.setVideoCodec(originalCodec);
                        
                        // Check if other parameters stop us copying the original video stream.
                        if(isVideoTranscodeRequired(parameters))
                        {
                            // Get a suitable video codec if original does not meet other parameters.
                            if(getSuitableVideoCodec(parameters.getFormat()) != null)
                            {
                                parameters.setVideoCodec(getSuitableVideoCodec(parameters.getFormat()));
                            }
                        }
                        // Copy the original video stream.
                        else
                        {
                            parameters.setVideoCodec("copy");
                            transcodeCommand.add("-c:v");
                            transcodeCommand.add("copy");
                        }
                    }
                    else
                    {
                        // Get a suitable video codec if original does not meet other parameters.
                        if(getSuitableVideoCodec(parameters.getFormat()) != null)
                        {
                            parameters.setVideoCodec(getSuitableVideoCodec(parameters.getFormat()));
                        }
                    }
                }
                
                if(!parameters.getVideoCodec().equals("copy"))
                {
                    if(getVideoCodecCommands(parameters.getVideoCodec()) != null)
                    {
                        transcodeCommand.addAll(getVideoCodecCommands(parameters.getVideoCodec()));
                    }
                }

                // Use all CPU cores
                transcodeCommand.add("-threads");
                transcodeCommand.add("0");

                // Output to std:out
                transcodeCommand.add("-");
                
                break;
                
            case AUDIO:
                
                // Input File
                transcodeCommand.add("-i");
                transcodeCommand.add(mediaElement.getFile().getAbsolutePath());
                
                // Audio Codec
                // If transcode is not required, simply copy audio stream.
                if(isAudioTranscodeRequired(parameters))
                {
                    if(getAudioCodecCommands(parameters) != null)
                    {
                        transcodeCommand.addAll(getAudioCodecCommands(parameters));
                    }

                    // Audio Channel Count / Quality
                    if(getTranscodedChannelCount(parameters) != null)
                    {
                        Integer channelCount = getTranscodedChannelCount(parameters);

                        // Test whether downmixing is necessary.
                        if(channelCount < getChannelCount(parameters.getMediaElement().getMetaData().getAudioConfiguration()))
                        {
                            transcodeCommand.add("-ac");
                            transcodeCommand.add(String.valueOf(channelCount));
                        }

                        // Stereo Audio Quality
                        if(channelCount == 2 && getAudioQualityCommands(parameters) != null)
                        {
                            transcodeCommand.addAll(getAudioQualityCommands(parameters));
                        }
                    }

                    // Audio Sample Rate
                    if(parameters.getMaxSampleRate() != null)
                    {
                        if(parameters.getMaxSampleRate() < mediaElement.getMetaData().getAudioSampleRate())
                        {
                            transcodeCommand.add("-ar");
                            transcodeCommand.add(String.valueOf(parameters.getMaxSampleRate()));
                        }
                    }
                }
                else
                {
                    transcodeCommand.add("-c:a");
                    transcodeCommand.add("copy");
                }

                transcodeCommand.add("-f");
                transcodeCommand.add(parameters.getFormat());
                
                transcodeCommand.add("-");
                
                break;
                
            default:
                return null;
                
        }
        
        return new TranscodeInputStream(new ProcessBuilder(transcodeCommand), null);
    }
    
     /************************************************************************
     *                          Transcode Utilities
     ***********************************************************************/
    
    /*
     * Returns the directory in which all transcoders are installed.
     */
    public File getTranscoder() 
    {
        File directory = new File(SettingsService.getSMSHome(), "transcode");
        
        if(!directory.exists())
        {
            LOG.warn("Transcode directory not found!");
            return null;
        }
            
        File transcoder = new File(directory, TRANSCODER);
            
        if(!transcoder.exists())
        {
            LOG.warn("Transcoder '" + TRANSCODER + "' not found!");
            return null;
        }
                
        return transcoder;
    }
    
    /*
     * Returns whether the given format is supported by the transcoder.
     */
    public boolean isSupportedFormat(String format)
    {
        switch(format)
        {
            // Supported Formats
            case "mp4": case "flac": case "mp3": case "ogg": case "wav": case "matroska": case "webm":
                return true;
                
            default:
                return false;
        }
    }
    
    /*
     * Returns whether the given codec is supported by the transcoder and the given format.
     */
    public boolean isSupportedCodec(String codec, String format)
    {
        switch(format)
        {
            case "matroska":
                return true;
                
            case "mp4":
                
                switch(codec)
                {
                    // MP4 Supported Video Codecs
                    case "h264": case "vc-1":
                        return true;
                    
                    // MP4 Supported Audio Codecs
                    case "aac": case "ac3": case "mp3":
                        return true;
                        
                    default:
                        return false;
                }
                
            case "webm":
                
                switch(codec)
                {
                    // WEBM Supported Video Codecs
                    case "vp8":
                        return true;
                     
                    // WEBM Supported Audio Codecs
                    case "vorbis":
                        return true;
                        
                    default:
                        return false;
                }
                
            default:
                return false;
        }
    }
    
    /************************************************************************
     *                          Video Utilities
     ***********************************************************************/
    
    /*
     * Returns a list of commands for a given video codec to optimise transcoding.
     */
    private Collection<String> getVideoCodecCommands(String videoCodec)
    {
        Collection<String> commands = new LinkedList<>();
        
        commands.add("-c:v");
        
        switch(videoCodec)
        {       
            case "vp8":
                commands.add("libvpx");
                commands.add("-quality");
                commands.add("realtime");
                commands.add("-cpu-used");
                commands.add("5");
                break;
                
            case "h264":
                commands.add("libx264");
                commands.add("-preset");
                commands.add("superfast");
                commands.add("-tune");
                commands.add("zerolatency");
                commands.add("-profile");
                commands.add("baseline");
                break;
                
            default:
                return null;
        }
        
        return commands;
    }
    
    /*
     * Returns a suitable video resolution based on the video quality requested.
     */
    public Dimension getSuitableVideoResolution(Integer quality, MediaElement element)
    {
        Dimension original = new Dimension(element.getMetaData().getWidth(), element.getMetaData().getHeight());
        Dimension requested;
        
        // Determine a template resolution based on the requested quality.
        switch(quality)
        {
            case 240:
                requested = new Dimension(426, 240);
                break;
                
            case 360:
                requested = new Dimension(640, 360);
                break;
                
            case 480:
                requested = new Dimension(854, 480);
                break;
                
            case 720:
                requested = new Dimension(1280, 720);
                break;
                
            case 1080:
                requested = new Dimension(1920, 1080);
                break;
                
            default:
                requested = new Dimension(640, 360);
        }

        // If the original resolution is less than the requested, return original resolution.
        if (original.width < requested.width || original.height < requested.height)
        {
            return new Dimension(even(original.width), even(original.height));
        }

        // Calculate the aspect ratio of the original video.
        double aspectRatio = new Integer(original.width).doubleValue() / new Integer(original.height).doubleValue();
        requested.height = (int) Math.round(requested.width / aspectRatio);

        return new Dimension(even(requested.width), even(requested.height));
    }
    
    /*
     * Returns a suitable video bit rate based on transcode resolution.
     */
    private Integer getSuitableVideoBitRate(Dimension resolution)
    {
        if(resolution.width <= 426 || resolution.height <= 240)
        {
            return 400;
        }
        else if(resolution.width <= 640 || resolution.height <= 360)
        {
            return 750;
        }
        else if(resolution.width <= 854 || resolution.height <= 480)
        {
            return 1000;
        }
        else if(resolution.width <= 1280 || resolution.height <= 720)
        {
            return 2500;
        }
        else if(resolution.width <= 1920 || resolution.height <= 1080)
        {
            return 4500;
        }
        
        return null;
    }

    /*
     * Make sure width and height are multiples of two for the transcoder.
     */
    private int even(int size)
    {
        return size + (size % 2);
    }
    
    /*
     * Returns a suitable video codec for a given format should one not be given.
     */
    private String getSuitableVideoCodec(String format)
    {
        switch(format)
        {
            case "matroska": case "mp4":
                return "h264";
                
            case "webm":
                return "vp8";
                
            default:
                return null;
        }
    }
    
    /*
     * Returns whether video transcoding is required based on given parameters.
     */
    private boolean isVideoTranscodeRequired(Parameters parameters)
    {
        String videoCodec = parameters.getVideoCodec();
        
        // If no video codec is given assume transcoding is required...
        if(videoCodec == null)
        {
            return true;
        }
        
        // Check if video codec match.
        if(videoCodec.equals(parameters.getMediaElement().getMetaData().getVideoCodec()))
        {   
            // Check if video quality is set in which case we need to transcode.
            if(parameters.getVideoQuality() != null)
            {
                return true;
            }
            
            // Transcoding is not required.
            return false; 
        }
        
        return true;
    }
    
    /************************************************************************
     *                          Audio Utilities
     ***********************************************************************/
    
    /*
     * Sets audio quality commands based on format.
     */
    private Collection<String> getAudioQualityCommands(Parameters parameters)
    {
        Collection<String> commands = new LinkedList<>();
        String audioCodecOrFormat = getAudioCodecOrFormat(parameters);
        
        // If the required parameters are not given return nothing.
        if(audioCodecOrFormat == null)
        {
            return null;
        }
        
        // We can't set a specific audio quality if it is not given.
        if(getAudioBitRate(parameters) == null)
        {
            return null;
        }
        
        switch(audioCodecOrFormat)
        {      
            case "mp4": case "aac":
                commands.add("-b:a");
                commands.add(getAudioBitRate(parameters) + "k");
                break;
                              
            case "mp3":
                commands.add("-b:a");
                commands.add(getAudioBitRate(parameters) + "k");
                break;
                
            case "ogg": case "vorbis":
                commands.add("-b:a");
                commands.add(getAudioBitRate(parameters) + "k");
                break;
                    
            default:
                return null;
        }
        
        return commands;
    }
    
    /*
     * Returns a list of commands for a given audio codec/format.
     */
    private Collection<String> getAudioCodecCommands(Parameters parameters)
    {
        Collection<String> commands = new LinkedList<>();
        String audioCodecOrFormat = getAudioCodecOrFormat(parameters);
        
        // If the required parameters are not given return nothing.
        if(audioCodecOrFormat == null)
        {
            return null;
        }
        
        commands.add("-c:a");
        
        switch(audioCodecOrFormat)
        {      
            case "mp4": case "aac":
                commands.add("libfdk_aac");
                break;
                
            case "ac3":
                commands.add("ac3");
                break;
             
            case "flac":
                commands.add("flac");
                break;
                    
            case "mp3":
                commands.add("libmp3lame");
                break;
                
            case "ogg": case "vorbis":
                commands.add("libvorbis");
                break;
               
            case "wav":
                commands.add("pcm_s16le");
                break;
                    
            default:
                return null;
        }
        
        return commands;
    }
    
    /*
     * Returns a suitable audio codec for a given format should one not be given.
     */
    private String getSuitableAudioCodec(String format)
    {
        switch(format)
        {
            case "matroska": case "mp4":
                return "ac3";
                
            case "webm":
                return "vorbis";
                
            default:
                return null;
        }
    }
    
    /*
     * Return number of channels present.
     */
    public Integer getChannelCount(String audioConfiguration)
    {
        // If no audio configuration return nothing.
        if(audioConfiguration == null)
        {
            return null;
        }
        
        switch(audioConfiguration)
        {
            case "stereo":
                return 2;
                
            case "quad":
                return 4;
                
            case "5.0":
                return 5;
                
            case "5.1":
                return 6;
                
            case "7.1":
                return 8;
                
            default:
                return null;
        }
    }
    
    /*
     * Returns either an audio codec or audio format depending on parameters given.
     */
    private String getAudioCodecOrFormat(Parameters parameters)
    {
        if(parameters.getAudioCodec() != null)
        {
            return parameters.getAudioCodec();
        }
        else
        {
            return parameters.getFormat();
        }
    }
    
    /*
     * Returns a bit rate for a given quality and audio format.
     */
    public Integer getAudioBitRate(Parameters parameters)
    {
        String audioCodecOrFormat = getAudioCodecOrFormat(parameters);
        
        // If the required parameters are not given return nothing.
        if(audioCodecOrFormat == null)
        {
            return null;
        }
        
        switch(audioCodecOrFormat)
        {
            case "aac": case "mp4":
                
                if(parameters.getAudioQuality() == null)
                {
                    return AAC_DEFAULT;
                }
                else
                {
                    switch(parameters.getAudioQuality())
                    {
                        case "low":
                            return AAC_LOW;

                        case "medium":
                            return AAC_MEDIUM;

                        case "high":
                            return AAC_HIGH;

                        default:
                            return AAC_DEFAULT;
                    }
                }
                              
            case "mp3":
                
                if(parameters.getAudioQuality() == null)
                {
                    return MP3_DEFAULT;
                }
                else
                {
                    switch(parameters.getAudioQuality())
                    {
                        case "low":
                            return MP3_LOW;

                        case "medium":
                            return MP3_MEDIUM;

                        case "high":
                            return MP3_HIGH;

                        default:
                            return MP3_DEFAULT;
                    }
                }
                
                
            case "ogg": case "vorbis":
                
                if(parameters.getAudioQuality() == null)
                {
                    return OGG_DEFAULT;
                }
                else
                {
                    switch(parameters.getAudioQuality())
                    {
                        case "low":
                            return OGG_LOW;

                        case "medium":
                            return OGG_MEDIUM;

                        case "high":
                            return OGG_HIGH;

                        default:
                            return OGG_DEFAULT;
                    }
                }
                    
            default:
                return null;
        }
    }
    
    /*
     * Returns the maximum channels supported by an audio format.
     */
    public int getMaximumSupportedChannels(Parameters parameters)
    {
        String audioCodecOrFormat = getAudioCodecOrFormat(parameters);
        
        // If the required parameters are not given return nothing.
        if(audioCodecOrFormat == null)
        {
            return 0;
        }
        
        switch(audioCodecOrFormat)
        {
            case "mp3":
                return 2;
                
            case "aac": case "mp4": case "ac3":
                return 6;
                
            case "flac": case "vorbis": case "ogg": case "wav":
                return 8;
                
            default:
                return 0;  
        }
    }
    
    /*
     * Returns the transcoded channel count based on given parameters.
     */
    public Integer getTranscodedChannelCount(Parameters parameters)
    {
        Integer sourceChannelCount = getChannelCount(parameters.getMediaElement().getMetaData().getAudioConfiguration());
        
        // If a max channel count is not specified return the source channel count.
        if(parameters.getMaxAudioChannels() == null)
        {
            return sourceChannelCount;
        }
        
        Integer channelCount = parameters.getMaxAudioChannels();

        // Test that the given audio codec supports the desired number of channels.
        if(channelCount > getMaximumSupportedChannels(parameters))
        {
            channelCount = getMaximumSupportedChannels(parameters);
        }

        // Test whether we need to downmix the original audio.
        if(channelCount > sourceChannelCount)
        {
            return sourceChannelCount;
        }
        else
        {
            return channelCount;
        }
    }
    
    /*
     * Returns whether audio transcoding is required based on given parameters.
     */
    private boolean isAudioTranscodeRequired(Parameters parameters)
    {
        String audioCodecOrFormat = getAudioCodecOrFormat(parameters);
        
        // If no audio codec is given assume transcoding is required...
        if(audioCodecOrFormat == null)
        {
            return true;
        }
        
        // Check if audio formats match.
        if(audioCodecOrFormat.equals(parameters.getMediaElement().getMetaData().getAudioCodec()))
        {
            // If number of channels is not suitable transcode is required.
            if(parameters.getMaxAudioChannels() != null)
            {
                if(parameters.getMaxAudioChannels() < getChannelCount(parameters.getMediaElement().getMetaData().getAudioConfiguration()))
                {
                    return true;
                }
            }
            
            // If sample rate is not suitable transcode is required.
            if(parameters.getMaxSampleRate() != null)
            {
                if(parameters.getMaxSampleRate() < parameters.getMediaElement().getMetaData().getAudioSampleRate())
                {
                    return true;
                }
            }
            
            // If this is a lossy codec, check the bitrate is not too high.
            if(getAudioBitRate(parameters) != null)
            {
                if(getAudioBitRate(parameters) < parameters.getMediaElement().getMetaData().getBitRate())
                {
                    return true;
                }
            }
            
            // Transcoding is not required.
            return false; 
        }
        
        return true;
    }
    
    public static class Parameters
    {
        private MediaElement mediaElement;
        private String format, videoCodec, audioCodec, audioQuality = null;
        private Integer maxAudioChannels, maxSampleRate = null;
        private Integer videoQuality = null;
        private Integer offset = null;
        
        //
        // Audio
        //
        public Parameters(MediaElement mediaElement, String format, Integer maxAudioChannels, Integer maxSampleRate, String audioQuality)
        {
            this.mediaElement = mediaElement;
            this.format = format;
            this.maxAudioChannels = maxAudioChannels;
            this.maxSampleRate = maxSampleRate;
            this.audioQuality = audioQuality;
        }
        
        //
        // Video
        //
        public Parameters(MediaElement mediaElement, String format, String videoCodec, String audioCodec, Integer maxAudioChannels, Integer maxSampleRate, Integer videoQuality, Integer offset)
        {
            this.mediaElement = mediaElement;
            this.format = format;
            this.videoCodec = videoCodec;
            this.audioCodec = audioCodec;
            this.maxAudioChannels = maxAudioChannels;
            this.maxSampleRate = maxSampleRate;
            this.videoQuality = videoQuality;
            this.offset = offset;
        }
        
        public MediaElement getMediaElement()
        {
            return mediaElement;
        }
        
        public void setMediaElement(MediaElement mediaElement)
        {
            this.mediaElement = mediaElement;
        }
        
        public String getFormat()
        {
            return format;
        }
        
        public void setFormat(String format)
        {
            this.format = format;
        }
        
        public String getVideoCodec()
        {
            return videoCodec;
        }
        
        public void setVideoCodec(String videoCodec)
        {
            this.videoCodec = videoCodec;
        }
        
        public String getAudioCodec()
        {
            return audioCodec;
        }
        
        public void setAudioCodec(String audioCodec)
        {
            this.audioCodec = audioCodec;
        }
        
        public Integer getMaxAudioChannels()
        {
            return maxAudioChannels;
        }
        
        public void setMaxAudioChannels(Integer maxAudioChannels)
        {
            this.maxAudioChannels = maxAudioChannels;
        }
        
        public Integer getMaxSampleRate()
        {
            return maxSampleRate;
        }
        
        public void setMaxSampleRate(Integer maxSampleRate)
        {
            this.maxSampleRate = maxSampleRate;
        }
        
        public Integer getVideoQuality()
        {
            return videoQuality;
        }
        
        public void setVideoQuality(Integer videoQuality)
        {
            this.videoQuality = videoQuality;
        }
        
        public String getAudioQuality()
        {
            return audioQuality;
        }
        
        public void setAudioQuality(String audioQuality)
        {
            this.audioQuality = audioQuality;
        }
        
        public Integer getOffset()
        {
            return offset;
        }
        
        public void setOffset(Integer offset)
        {
            this.offset = offset;
        }
    }
}